
#include "ipciomodule.h"


PyObject *
PySockAddr_New(int family)
{
	PySockAddrObject *addrobj;
	Py_ssize_t size;

	switch (family)
	{
	case AF_INET:
		size = sizeof(struct sockaddr_in);
		break;
	case AF_INET6:
		size = sizeof(struct sockaddr_in6);
		break;
	case AF_UNIX:
		size = sizeof(struct sockaddr_un);
		break;
	default:
		return PyErr_Format(PyExc_ValueError, "unsupported address family");
	}
	addrobj = PyObject_NewVar(PySockAddrObject, &PySockAddr_Type, size);
	if (addrobj)
	{
		memset(addrobj->ob_item, 0, size);
		PySockAddr_GET_ADDR(addrobj)->sa_family = family;
	}
	return (PyObject *) addrobj;
}

PyObject *
PySockAddr_FromAddr(const void *addr, Py_ssize_t size)
{
	PySockAddrObject *addrobj;

	assert(addr != NULL);
	addrobj = PyObject_NewVar(PySockAddrObject, &PySockAddr_Type, size);
	if (addrobj)
		Py_MEMCPY(addrobj->ob_item, addr, size);
	return (PyObject *) addrobj;
}

Py_LOCAL(int)
_sockaddr_getsocktype(const char *mode)
{
	if (strchr(mode, 's'))
		return SOCK_STREAM;
	else if (strchr(mode, 'd'))
		return SOCK_DGRAM;
	else if (strchr(mode, 'm'))
		return SOCK_RDM;
	else if (strchr(mode, 'p'))
		return SOCK_SEQPACKET;
	else
		return 0;
}

typedef (PyObject *) (*sockaddr_resolver_t)(const Py_UNICODE *addr, 
					  const char *mode, const char *scheme,
					  int family, int flags, int all);

typedef struct {
	const Py_UNICODE *addr;
	size_t addrlen;
	const char *mode;
	const char *scheme;
	int family;
	int flags;
	int all;
} sockaddr_nsinfo_t;

typedef struct {
	const char *ns_name;
	int ns_family;
	sockaddr_resolver_t ns_resolve;
} sockaddr_nsmap_t;

static const sockaddr_nsmap_t sockaddr_nsmap[] = 
{
	{"inet",  AF_UNSPEC, gai_resolve},
	{"inet4", AF_INET,   gai_resolve},
	{"inet6", AF_INET6,  gai_resolve},
	{"local", AF_LOCAL,  unix_resolve},
	{"unix",  AF_UNIX,   unix_resolve},
	{"atalk", AF_APPLETALK, nbp_resolve},
	{NULL}
};


#ifdef HAVE_GAI

PyObject *
gai_resolve(sockaddr_nsinfo_t *nsinfo)
{
	PyObject *addrobj, *listobj, *resobj = NULL;
	char *delim;
	const char *host, *port;
//	unsigned long scopeid = 0;
	struct addrinfo aihint = {0}, *aihead = NULL, *aiptr;
	int res, count;

	aihint.ai_family = nsinfo->family;
	aihint.ai_socktype = sockaddr_getsocktype(nsinfo->mode);
	if (nsinfo->flags == -1)
	{
		aihint.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED;
		if (nsinfo->all)
			aihint.ai_flags |= AI_ALL;
		if (strchr(nsinfo->mode, '@'))
			aihint.ai_flags |= AI_PASSIVE;
	}
	else
		aihint.ai_flags = nsinfo->flags;

	if (*addrptr == '[')
	{
		addrptr += 1;
		delmptr = Py_unichr(addrptr, addrend - addrptr, ']');
		if (!delmptr || delmptr[1] != ':')
			return PyErr_Format(PyExc_ValueError, "bad inet6 addr format");
		hostobj = PyUnicode_EncodeASCII(addrptr, delmptr - addrptr, NULL);
		if (!hostobj)
			return NULL;
		if (nsinfo->flags == -1)
			aihint.ai_flags |= AI_NUMERICHOST;
		addrptr = delmptr + 2;
	}
	else
	{
		delmptr = Py_unichr(addrptr, addrend - addrptr, ':');
		if (!delmptr)
			return PyErr_Format(PyExc_ValueError, "bad inet addr format");
		hostobj = PyUnicode_Encode(addrptr, delmptr - addrptr, "idna", NULL);
		if (!hostobj)
			return NULL;
		if (flags == -1 && Py_unispn(addrptr, addrend - addrptr, ASCII_DIGITS ".") == delim - addr)
			aihint.ai_flags |= AI_NUMERICHOST;
		addr = delim + 1;
	}
	servobj = PyUnicode_EncodeASCII(addr, foo, NULL);
	if (!servobj)
		foo;
	hostptr = PyString_GET_SIZE(hostobj) ? PyString_AS_STRING(hostob) : NULL;
	servptr = 

	if (*host == '\0')
		host = NULL;
	port = addrptr;
	delim = strpbrk(addrptr, "/?#");
	if (delim)
		*delim = '\0';
	if (flags == -1 && port[strspn(port, "0123456789")] == '\0')
		aihint.ai_flags |= AI_NUMERICSERV;

    Py_BEGIN_ALLOW_THREADS
	res = getaddrinfo(host, port, &aihint, &aihead);
    Py_END_ALLOW_THREADS

	if (res && (!all || res != EAI_NONAME))
		foo;

	if (all)
	{
		for (count = 0, aiptr = aihead; aiptr; aiptr = aiptr->ai_next)
			count++;
		listobj = PyList_New(count);
		if (!listobj)
			goto done;
	}
	for (count = 0, aiptr = aihead; aiptr; aiptr = aiptr->ai_next)
	{
		addrobj = PySockAddr_FromAddr(aiptr->ai_addr, aiptr->ai_addrlen);
		if (!addrobj || !all)
			break;
//			memcpy(PySockAddr_Addr(addrobj), aiptr->ai_addr, aiptr->ai_addrlen);
//		if (!all)
//			break;
		PyList_SET_ITEM(listobj, count++, addrobj);
	}

	if (all)
	{
		const char *canonname = aihead ? aihead->ai_canonname : NULL;
		if (!aiptr)
			resobj = Py_BuildValue("zN", canonname, listobj);
		else
			Py_DECREF(listobj);
	}
	else
		resobj  = addrobj;
done:
	freeaddrinfo(aihead);
	return resobj;
}
#endif /* HAVE_INET */

#ifdef HAVE_UNIX
PyObject *
_sockaddr_from_unix(char *addrptr, const char *mode, 
					 int family, int flags, int all)
{
	PyObject *addrobj;
	char *delim;
	const char *path;
	size_t pathlen;
	struct sockaddr_sun *sunptr;

	path = addrptr;
	delim = strpbrk(addrptr, "?#");
	if (delim)
		*delim = '\0';
	pathlen = strlen(path);
	if (pathlen >= sizeof(sunptr->sun_path))
		return PyErr_Format(PyExc_ValueError, "path too long");

	addrobj = PySockAddr_New(family);
//	addrobj = PySockAddr_FromAddr(NULL, pathlen + 
//								  offsetof(struct sockaddr_sun, sun_path));
	if (!addrobj)
		return NULL;
	PySockAddr_GET_SIZE(addrobj) = pathlen + offsetof(struct sockaddr_sun, sun_path);
	sunptr = (struct sockaddr_sun *) PySockAddr_GET_ADDR(addrobj);
//	sunptr->sun_family = AF_UNIX;
//	memcpy(sunptr->sun_path, path, pathlen);
	strcpy(sunptr->sun_path, path);
	/* check for Linux abstract namespace */
	if (sunptr->sun_path[0] == '!')
		sunptr->sun_path[0] = '\0';

	if (all)
	{
		char canonpath[PATH_MAX+1];
		const char *canonname = realpath(path, canonpath);
		return Py_BuildValue("z[N]", canonname, addrobj);
	}
	else
		return addrobj;
}
#endif /* HAVE_UNIX */

PyObject *
sockaddr_resolve(const Py_UNICODE *addr, const char *mode, 
				 const char *scheme, int flags, int all)
{
	const sockaddr_nsmap_t *nsmptr = sockaddr_nsmap;
	Py_ssize_t addrlen;
	
	addrlen = Py_wcsspn(addr, ASCII_ALNUM "_");
	if (addr[addrlen] == '=')
	{
		PyObject *nsobj = PyUnicode_EncodeASCII(addr, addrlen, NULL);
		if (!nsobj)
			return NULL;
		for (; nsmptr->ns_name; nsmptr++)
			if (strcmp(nsmptr->ns_name, PyString_AS_STRING(nsobj)) == 0)
				break;
		Py_DECREF(nsobj);
		if (!nsmptr->ns_name)
			return PyErr_Format(PyExc_ValueError, "bad name space");
		addr += addrlen + 1;
	}
	return nsmptr->ns_resolve(addr, mode, scheme, nsmptr->ns_family, flags, all);
}

PyObject *
PySockAddr_FromName(const Py_UNICODE *name, const char *mode, int flags)
{
	return sockaddr_resolve(name, mode, NULL, flags, 0);
}

PyObject *
PySockAddr_ListAllFromName(const Py_UNICODE *name, const char *mode, int flags)
{
	return sockaddr_resolve(name, mode, NULL, flags, 1);
}

PyObject *
PySockAddr_GetName(PyObject *self, int flags)
{
	PyObject *resobj;
	int family;

    if (!PySockAddr_Check(self))
        return PyErr_BadArgument(), NULL;

	family = PySockAddr_GET_ADDR(self)->sa_family;
	switch (family)
	{
	case AF_INET:
	case AF_INET6:
		return _sockaddr_make
	}
}

/*
	/* skip over 'scheme://' or '//' parts *
//	addrlen = wcsspn(addrptr, L"abcdefghijklmnopqrstuvwxyz0123456789+-.");
	delim = addrptr + wcsspn(addrptr, L"abcdefghijklmnopqrstuvwxyz0123456789+-.");
	if (wcscmp(delim, L"://") == 0)
	{
		scheme = addrptr;
		*delim = L'\0';
		addrptr = delim + 3;
	}
	else if (wcscmp(addrptr, "//") == 0)
		addrptr += 2;
	* find the end of the authority part *
	if (addrptr != namebuf)
	{
		endptr = addrptr;
		while (*endptr)
		{
			endptr += wcscspn(endptr, L"[/?#");
			if (*endptr == L'[')
				endptr = wcschr(endptr, L']');
			else
				*endptr = L'\0';
		}
	}
	/* skip over 'userinfo@' part *
	delim = strchr(addrptr, '@');
	if (delim && (!endptr || delim < endptr))
		addrptr = delim + 1;



wchar_t namebuf[foo];
	PyUnicode_AsWideChar(name, namebuf, foo);
	
	while (ch < 0x80 && (isalnum(ch) || ch == '-' || ch == '+' || ch == '.'))


//	addrarg = strdup(name);
//	if (!addrarg)
//		return PyErr_NoMemory();
//	addrptr = addrarg;
	/* search for 'family=' part *
	delim = strchr(addrptr, '=');
	if (delim && (!endptr || delim < endptr))
	{
		*delim = '\0';
		fmlyptr = addrptr;
		addrptr = delim + 1;
	}
	else
	{
		/* if the family isn't specified and addr starts with '/', 
		   then let's assume it is AF_UNIX *
		if (*addrptr == '/')
			fmlyptr = "unix";
		else
			fmlyptr = "";
	}

	/* this will return -1 and raise an exception 
	   if family not found or not an int *
	family = PyInt_AsLong(PyDict_GetItemString(foo, fmlyptr));
	switch (family)
	{
#ifdef HAVE_INET
	case AF_UNSPEC:
#ifdef AF_INET
	case AF_INET:
#endif
#ifdef AF_INET6
	case AF_INET6:
#endif
		resobj = _sockaddr_from_inet(addrptr, mode, family, flags, all);
		break;
#endif
#ifdef HAVE_UNIX
	case AF_UNIX:
		resobj = _sockaddr_from_unix(addrptr, mode, family, flags, all);
		break;
#endif
	/* specify -1 explicitly to make sure no AF_* has the same value *
	case -1:
	default:
		PyErr_SetString(PyExc_ValueError, "bad address family");
	}
	free(addrarg);
	return resobj;
}
*/
static PyObject *
ipcio_resolve(PyObject *self, PyObject *args, PyObject *kwds)
{
	const char *kwlist[] = {"addr", "mode", "allresults", "flags", NULL};
	const char *mode = "s";
	int allres = 1, flags = -1, family = -1;
	char *addrptr, *endptr, *delim, *domain = NULL;
	size_t addrlen;
	PyObject *resobj = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|sii:resolve", kwlist,
									 "idna", &addrarg, &mode, &allres, &flags))
		return NULL;

}

static PyObject *
sockaddr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    PySockAddrObject *self;

    assert(type != NULL && type->tp_alloc != NULL);
    self = (PySockAddrObject *) type->tp_alloc(type, 0);
	if (self)
	{
	}
	return (PyObject *) self;
}

static int
sockaddr_init(PySockAddrObject *self, PyObject *args, PyObject *kwds)
{
//	PySocketObject *self = (PySocketObject*) oself;
	static char *kwlist[] = {"socket", "mode", "closefd", "timeout", 
							 "backlog", "openargs", NULL};
	PyObject *nameobj;

	assert(PySockAddr_Check(self));

	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|si:SockAddr",
									 kwlist, &nameobj, &mode, &closefd))
		return -1;
}

static PyMethodDef sockaddr_methods[] =
{
    {"getsockopt",  (PyCFunction) socketio_getsockopt,  METH_VARARGS, getsockopt_doc},
	{0}
};

PyTypeObject PySockAddr_Type =
{
	PyVarObject_HEAD_INIT(NULL, 0)
	"_ipcio.SockAddr",                          /* tp_name */
	sizeof(PySockAddrObject) - sizeof(Py_align_t), /* tp_basicsize */
	1,                                          /* tp_itemsize */
	(destructor) sockaddr_dealloc,              /* tp_dealloc */
	0,                                          /* tp_print */
	0,                                          /* tp_getattr */
	0,                                          /* tp_setattr */
	0,                                          /* tp_reserved */
	(reprfunc) sockaddr_repr,                   /* tp_repr */
	0,                                          /* tp_as_number */
	0,                                          /* tp_as_sequence */
	0,                                          /* tp_as_mapping */
	(hashfunc) sockaddr_hash,                   /* tp_hash */
	0,                                          /* tp_call */
	0,                                          /* tp_str */
	PyObject_GenericGetAttr,                    /* tp_getattro */
	0,                                          /* tp_setattro */
	0,                                          /* tp_as_buffer */
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,    /* tp_flags */
	sockaddr_doc,                               /* tp_doc */
	0,           /* tp_traverse */
	0,                   /* tp_clear */
	0,                                          /* tp_richcompare */
	0,                /* tp_weaklistoffset */
	0,                                          /* tp_iter */
	0,                                          /* tp_iternext */
	sockaddr_methods,                           /* tp_methods */
	0,                                          /* tp_members */
	sockaddr_getsets,                           /* tp_getset */
	0,                                          /* tp_base */
	0,                                          /* tp_dict */
	0,                                          /* tp_descr_get */
	0,                                          /* tp_descr_set */
	0,                   /* tp_dictoffset */
	sockaddr_init,                              /* tp_init */
	PyType_GenericAlloc,                        /* tp_alloc */
	sockaddr_new,                               /* tp_new */
	PyObject_Del,                               /* tp_free */
};
